﻿#region using
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;

using XmlGenSharp.Logic.Main.Configuration;
#endregion

namespace XmlGenSharp.Logic.Main
{
  public class ClassGenerator : GeneratorBase
  {
    // Constructors

    public ClassGenerator(string inputFileName, string defaultNamespace, XmlSchemaComplexType complexType) :
      this(inputFileName, defaultNamespace, complexType.Name, complexType)
    {
    }

    #region ClassGenerator(string inputFileName, string defaultNamespace, string baseName, XmlSchemaComplexType complexType)
    public ClassGenerator(string inputFileName, string defaultNamespace, string baseName, XmlSchemaComplexType complexType)
      : base(inputFileName, defaultNamespace)
    {
      BaseName = baseName;
      ComplexType = complexType;
    }
    #endregion

    // Public Properties

    #region BaseName
    public string BaseName
    {
      get;
      set;
    }
    #endregion

    #region ComplexType
    public XmlSchemaComplexType ComplexType
    {
      get;
      protected set;
    }
    #endregion

    #region OverwriteIfExists
    public override bool OverwriteIfExists
    {
      get
      {
        return true;
      }
    }
    #endregion

    // Public Methods

    #region GetFileName()
    public override string GetFileName()
    {
      string baseFileName = Path.GetFileNameWithoutExtension(InputFileName);

      if (String.IsNullOrEmpty(BaseName))
        return baseFileName + ".Generator.cs";

      return baseFileName + ".Class." + BaseName + ".Generator.cs";
    }
    #endregion

    #region Generate(IGeneratorContext cx)
    public override void Generate(IGeneratorContext cx)
    {
      Section.IdCounter = 0;

      cx.W(PreText);

      cx.WL("");
      cx.WL("  public partial class " + ComplexType.Name);
      cx.WL("  {");

      GenerateConstructors(cx);
      cx.WL("");
      GenerateProperties(cx);
      GenerateLoadMethods(cx);
      GenerateParseNodesMethod(cx);

      cx.WL("  }");

      cx.W(PostText);
    }
    #endregion

    // Private Methods

    #region GenerateConstructors(IGeneratorContext cx)
    private void GenerateConstructors(IGeneratorContext cx)
    {
      cx.WL("    // Constructors");

      cx.WL("");
      cx.WL("    #region " + ComplexType.Name + "()");
      cx.WL("    public " + ComplexType.Name + "()");
      cx.WL("    {");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region " + ComplexType.Name + "(string filename)");
      cx.WL("    public " + ComplexType.Name + "(string filename)");
      cx.WL("    {");
      cx.WL("      XmlDocument xml = new XmlDocument();");
      cx.WL("      xml.Load(filename);");
      cx.WL("      LoadFromXmlNode(xml.DocumentElement);");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region " + ComplexType.Name + "(XmlDocument xml)");
      cx.WL("    public " + ComplexType.Name + "(XmlDocument xml)");
      cx.WL("    {");
      cx.WL("      LoadFromXmlNode(xml.DocumentElement);");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region " + ComplexType.Name + "(XmlNode node)");
      cx.WL("    public " + ComplexType.Name + "(XmlNode node)");
      cx.WL("    {");
      cx.WL("      LoadFromXmlNode(node);");
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateProperties(IGeneratorContext cx)
    private void GenerateProperties(IGeneratorContext cx)
    {
      cx.WL("    // Public Properties");

      cx.WL("");
      cx.WL("    #region XmlNode");
      cx.WL("    public XmlNode XmlNode");
      cx.WL("    {");
      cx.WL("      get;");
      cx.WL("      protected set;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region TextValue");
      cx.WL("    public string TextValue");
      cx.WL("    {");
      cx.WL("      get");
      cx.WL("      {");
      cx.WL("        return this.XmlNode.InnerText;");
      cx.WL("      }");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      GenerateAttributeProperties(cx);
      GenerateElementProperties(cx);
    }
    #endregion

    #region GenerateLoadMethods(IGeneratorContext cx)
    private void GenerateLoadMethods(IGeneratorContext cx)
    {
      cx.WL("    // Private Methods");
      cx.WL("");
      cx.WL("    #region LoadFromXmlNode(XmlNode node)");
      cx.WL("    private void LoadFromXmlNode(XmlNode node)");
      cx.WL("    {");
      cx.WL("      XmlNode = node;");

      foreach (XmlSchemaAttribute attrib in ComplexType.Attributes)
      {
        string propName = MakePropertyName(ComplexType.Name, attrib.Name);
        string attribTypeName = ToDotNetTypeName(attrib.SchemaTypeName.Name);
        TypeHelper(attrib.SchemaTypeName.Name).GenerateAttributeLoader(cx, this, propName, attrib, attrib.Use);
      }

      cx.WL("");
      cx.WL("      ParseNodes(node);");

      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateParseNodesMethod(IGeneratorContext cx)
    private void GenerateParseNodesMethod(IGeneratorContext cx)
    {
      XmlSchemaSequence sequence = ComplexType.ContentTypeParticle as XmlSchemaSequence;
      XmlSchemaAll all = ComplexType.ContentTypeParticle as XmlSchemaAll;
      XmlSchemaChoice choice = ComplexType.ContentTypeParticle as XmlSchemaChoice;
      XmlSchemaObjectCollection items = null;
      XmlSchemaGroupBase groupBase = ComplexType.ContentTypeParticle as XmlSchemaGroupBase;
      Section mainSection = null;

      if (sequence != null)
      {
        mainSection = new SequenceSection(this);
        items = sequence.Items;
      }
      else if (all != null)
      {
        mainSection = new AllSection(this);
        items = all.Items;
      }
      else if (choice != null)
      {
        mainSection = new ChoiceSection(this);
        items = choice.Items;
      }

      if (groupBase != null && groupBase.Items != null && mainSection != null)
      {
        List<Section> sections = SplitIntoSections(cx, groupBase.Items);
        mainSection.SubSections = sections;
        mainSection.GenerateParseNodesMethod(cx);
      }

      cx.WL("");
      cx.WL("    #region ParseNodes(XmlNode parent)");
      cx.WL("    private void ParseNodes(XmlNode parent)");
      cx.WL("    {");

      if (mainSection != null)
      {
        cx.WL("      IEnumerator enumerator = parent.ChildNodes.GetEnumerator();");
        cx.WL("      bool more = enumerator.MoveNext();");
        cx.WL("      ParseSection1Nodes(parent, ref enumerator, ref more);");
      }

      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateAttributeProperties(IGeneratorContext cx)
    private void GenerateAttributeProperties(IGeneratorContext cx)
    {
      foreach (XmlSchemaAttribute attrib in ComplexType.Attributes)
      {
        string propName = MakePropertyName(ComplexType.Name, attrib.Name);

        cx.WL("    #region " + propName);
        cx.WL("    public " + ToDotNetTypeName(attrib.SchemaTypeName.Name) + " " + propName);
        cx.WL("    {");
        cx.WL("      get;");
        cx.WL("      protected set;");
        cx.WL("    }");
        cx.WL("    #endregion");
        cx.WL("");
      }
    }
    #endregion

    #region GenerateElementProperties(IGeneratorContext cx)
    private void GenerateElementProperties(IGeneratorContext cx)
    {
      if (ComplexType.ContentTypeParticle is XmlSchemaSequence)
        GenerateSequenceProperties(cx, ComplexType.ContentTypeParticle as XmlSchemaSequence);
      else if (ComplexType.ContentTypeParticle is XmlSchemaAll)
        GenerateAllProperties(cx, ComplexType.ContentTypeParticle as XmlSchemaAll);
      else if (ComplexType.ContentTypeParticle is XmlSchemaChoice)
        GenerateChoiceProperties(cx, ComplexType.ContentTypeParticle as XmlSchemaChoice);
    }
    #endregion

    #region GenerateSequenceProperties(IGeneratorContext cx, XmlSchemaSequence sequence)
    private void GenerateSequenceProperties(IGeneratorContext cx, XmlSchemaSequence sequence)
    {
      GenerateProperties(cx, sequence.Items);
    }
    #endregion

    #region GenerateAllProperties(IGeneratorContext cx, XmlSchemaAll all)
    private void GenerateAllProperties(IGeneratorContext cx, XmlSchemaAll all)
    {
      GenerateProperties(cx, all.Items);
    }
    #endregion

    #region GenerateChoiceProperties(IGeneratorContext cx, XmlSchemaChoice choice)
    private void GenerateChoiceProperties(IGeneratorContext cx, XmlSchemaChoice choice)
    {
      GenerateProperties(cx, choice.Items);
    }
    #endregion

    #region GenerateProperties(IGeneratorContext cx, XmlSchemaObjectCollection objects)
    private void GenerateProperties(IGeneratorContext cx, XmlSchemaObjectCollection objects)
    {
      foreach (XmlSchemaObject child in objects)
      {
        if (child is XmlSchemaSequence)
        {
          GenerateSequenceProperties(cx, child as XmlSchemaSequence);
          continue;
        }
        else if (child is XmlSchemaAll)
        {
          GenerateAllProperties(cx, child as XmlSchemaAll);
          continue;
        }
        else if (child is XmlSchemaChoice)
        {
          GenerateChoiceProperties(cx, child as XmlSchemaChoice);
          continue;
        }

        XmlSchemaElement childElement = child as XmlSchemaElement;
        if (childElement == null)
          continue;

        string dotNetTypeName = ToDotNetTypeName(childElement.ElementSchemaType.Name);
        string defType = dotNetTypeName;

        if (cx.Config != null && cx.Config.ClassTable != null)
        {
          ClassConfigData ccd = cx.Config.ClassTable.FindClass(dotNetTypeName);
          if (ccd != null && !String.IsNullOrEmpty(ccd.DefType))
            defType = ccd.DefType;
        }

        try
        {
          if (childElement.MaxOccurs > 1 || childElement.MaxOccursString.ToLower() == "unbounded")
          {
            string propName = MakePluralPropertyName(ComplexType.Name, childElement.Name);
            string camelName = ToCamelCase(propName);

            // More than one occurrence is allowed, so generate a list of those items
            cx.WL("    #region " + propName);
            cx.WL("    public List<" + defType + "> " + propName);
            cx.WL("    {");
            cx.WL("      get");
            cx.WL("      {");
            cx.WL("        if (_" + camelName + " == null)");
            cx.WL("          _" + camelName + " = new List<" + defType + ">();");
            cx.WL("");
            cx.WL("        return _" + camelName + ";");
            cx.WL("      }");
            cx.WL("      protected set");
            cx.WL("      {");
            cx.WL("        _" + camelName + " = value;");
            cx.WL("      }");
            cx.WL("    }");
            cx.WL("    private List<" + defType + "> _" + camelName + " = null;");
            cx.WL("    #endregion");
            cx.WL("");
          }
          else
          {
            string propName = MakePropertyName(ComplexType.Name, childElement.Name);
            string camelName = ToCamelCase(propName);

            // Only one occurrence is allowed, so just generate a property
            cx.WL("    #region " + propName);
            cx.WL("    public " + defType + " " + propName);
            cx.WL("    {");
            cx.WL("      get;");
            cx.WL("      protected set;");
            cx.WL("    }");
            cx.WL("    #endregion");
            cx.WL("");
          }
        }
        catch (Exception ex)
        {
          cx.WL("    ERROR GENERATING PROPERTY");
          cx.WL("    // {0}", ex.Message);
          cx.WL("    // childElement.ElementSchemaType = '{0}'", (childElement.ElementSchemaType == null) ? "null" : "not null");

          if (childElement.ElementSchemaType != null)
            cx.WL("    // childElement.ElementSchemaType.Name = '{0}'", (childElement.ElementSchemaType.Name == null) ? "null" : childElement.ElementSchemaType.Name);

          cx.WL("    // childElement.SchemaType        = '{0}'", (childElement.SchemaType == null) ? "null" : "not null");

          if (childElement.SchemaType != null)
            cx.WL("    // childElement.SchemaType.Name = '{0}'", childElement.SchemaType.Name);
        }
      }
    }
    #endregion

    #region SplitIntoSections(IGeneratorContext cx, XmlSchemaObjectCollection objects)
    private List<Section> SplitIntoSections(IGeneratorContext cx, XmlSchemaObjectCollection objects)
    {
      List<Section> sections = new List<Section>();
      ElementsSection current = null;

      foreach (XmlSchemaObject obj in objects)
      {
        if (obj is XmlSchemaElement)
        {
          if (current == null)
          {
            current = new ElementsSection(this);
            sections.Add(current);
          }

          current.Elements.Add(obj as XmlSchemaElement);
        }
        else
        {
          if (obj is XmlSchemaSequence)
          {
            XmlSchemaSequence sequence = obj as XmlSchemaSequence;
            SequenceSection seqSection = new SequenceSection(this);
            seqSection.SubSections = SplitIntoSections(cx, sequence.Items);
            sections.Add(seqSection);
          }
          else if (obj is XmlSchemaAll)
          {
            XmlSchemaAll all = obj as XmlSchemaAll;
            AllSection allSection = new AllSection(this);
            allSection.SubSections = SplitIntoSections(cx, all.Items);
            sections.Add(allSection);
          }
          else if (obj is XmlSchemaChoice)
          {
            XmlSchemaChoice choice = obj as XmlSchemaChoice;
            ChoiceSection choiceSection = new ChoiceSection(this);
            choiceSection.SubSections = SplitIntoSections(cx, choice.Items);
            sections.Add(choiceSection);
          }

          current = null;
        }
      }

      return sections;
    }
    #endregion

    // Private Classes

    #region class Section
    private abstract class Section
    {
      public static int IdCounter = 0;
      public List<Section> SubSections = null;
      public int Id = ++IdCounter;

      // Public Constructors

      #region Section(ClassGenerator classGenerator)
      public Section(ClassGenerator classGenerator)
      {
        ClassGenerator = classGenerator;
      }
      #endregion

      // Public Properties

      #region ClassGenerator
      public ClassGenerator ClassGenerator
      {
        get;
        protected set;
      }
      #endregion

      #region ElementNames
      public List<string> ElementNames
      {
        get
        {
          List<string> names = new List<string>();
          AddElementNames(names);

          return names;
        }
      }
      #endregion

      #region ComplexType
      public XmlSchemaComplexType ComplexType
      {
        get
        {
          return ClassGenerator.ComplexType;
        }
      }
      #endregion

      // Abstract Methods

      #region void GenerateParseNodesMethod(IGeneratorContext cx);
      public abstract void GenerateParseNodesMethod(IGeneratorContext cx);
      #endregion

      // Public Methods

      #region AddElementNames(List<string> names)
      public virtual void AddElementNames(List<string> names)
      {
        if (SubSections == null)
          return;

        foreach (Section subSection in SubSections)
          subSection.AddElementNames(names);
      }
      #endregion
    }
    #endregion

    #region class SequenceSection
    private class SequenceSection : Section
    {
      // Public Constructors

      #region SequenceSection(ClassGenerator classGenerator)
      public SequenceSection(ClassGenerator classGenerator) : base(classGenerator)
      {
      }
      #endregion

      // Public Methods

      #region GenerateParseNodesMethod(IGeneratorContext cx)
      public override void GenerateParseNodesMethod(IGeneratorContext cx)
      {
        cx.WL("");
        cx.WL("    #region ParseSection{0}Nodes(XmlNode parent, ref IEnumerator enumerator, ref bool more)", Id);
        cx.WL("    private void ParseSection{0}Nodes(XmlNode parent, ref IEnumerator enumerator, ref bool more)", Id);
        cx.WL("    {");

        int seqState = 0;

        cx.WL("      int seqState = 0;");
        cx.WL("");
        cx.WL("      for (bool done=false; more && !done;)");
        cx.WL("      {");
        cx.WL("        XmlNode node = enumerator.Current as XmlNode;");
        cx.WL("        if (node.NodeType == XmlNodeType.Comment)");
        cx.WL("        {");
        cx.WL("          more = enumerator.MoveNext();");
        cx.WL("          continue;");
        cx.WL("        }");
        cx.WL("");
        cx.WL("        switch (node.Name.ToLower())");
        cx.W("        {");

        foreach (Section subSection in SubSections)
        {
          ElementsSection es = subSection as ElementsSection;
          if (es != null)
          {
            foreach (XmlSchemaElement element in es.Elements)
            {
              cx.WL("");
              cx.WL("          case \"{0}\":", element.Name.ToLower());
              cx.WL("            if (seqState > {0})", seqState);
              cx.WL("              throw new Exception(\"Elements out of sequence\");");
              cx.WL("");
              cx.WL("            seqState = {0};", seqState);


              if (element.MaxOccurs > 1 || element.MaxOccursString.ToLower() == "unbounded")
              {
                string propName = ClassGenerator.MakePluralPropertyName(ComplexType.Name, element.Name);

                cx.WL("");
                cx.WL("            {1} elem{0} = new {1}(node);", element.Name, element.ElementSchemaType.Name);
                cx.WL("            if (elem{0} != null)", element.Name);
                cx.WL("              {0}.Add(elem{1});", propName, element.Name);
              }
              else
              {
                string propName = ClassGenerator.MakePropertyName(ComplexType.Name, element.Name);

                cx.WL("            {0} = new {1}(node);", propName, element.ElementSchemaType.Name);
              }

              cx.WL("            break;");

              seqState++;
            }
          }
          else
          {
            cx.WL("");

            foreach (string name in subSection.ElementNames)
              cx.WL("          case \"{0}\":", name.ToLower());

            cx.WL("            if (seqState > {0})", seqState);
            cx.WL("              throw new Exception(\"Elements out of sequence\");");
            cx.WL("");
            cx.WL("            seqState = {0};", seqState);
            cx.WL("            ParseSection{0}Nodes(parent, ref enumerator, ref more);", subSection.Id);
            cx.WL("            break;");
          }

          seqState++;
        }

        cx.WL("");
        cx.WL("          default:");
        cx.WL("            done = true;");
        cx.WL("            break;");

        cx.WL("        }");
        cx.WL("");
        cx.WL("        if (!done)");
        cx.WL("          more = enumerator.MoveNext();");
        cx.WL("      }");
        cx.WL("    }");
        cx.WL("    #endregion");

        foreach (Section subSection in SubSections)
          subSection.GenerateParseNodesMethod(cx);
      }
      #endregion
    }
    #endregion

    #region class AllSection
    private class AllSection : Section
    {
      // Public Constructors

      #region AllSection(ClassGenerator classGenerator)
      public AllSection(ClassGenerator classGenerator) : base(classGenerator)
      {
      }

      #endregion

      // Public Methods

      #region GenerateParseNodesMethod(IGeneratorContext cx)
      public override void GenerateParseNodesMethod(IGeneratorContext cx)
      {
        cx.WL("");
        cx.WL("    #region ParseSection{0}Nodes(XmlNode parent, ref IEnumerator enumerator, ref bool more)", Id);
        cx.WL("    private void ParseSection{0}Nodes(XmlNode parent, ref IEnumerator enumerator, ref bool more)", Id);
        cx.WL("    {");

        cx.WL("      for (bool done=false; more && !done;)");
        cx.WL("      {");
        cx.WL("        XmlNode node = enumerator.Current as XmlNode;");
        cx.WL("        if (node.NodeType == XmlNodeType.Comment)");
        cx.WL("        {");
        cx.WL("          more = enumerator.MoveNext();");
        cx.WL("          continue;");
        cx.WL("        }");
        cx.WL("");
        cx.WL("        switch (node.Name.ToLower())");
        cx.W("        {");

        foreach (Section subSection in SubSections)
        {
          ElementsSection es = subSection as ElementsSection;
          if (es != null)
          {
            foreach (XmlSchemaElement element in es.Elements)
            {
              cx.WL("");
              cx.WL("          case \"{0}\":", element.Name.ToLower());


              if (element.MaxOccurs > 1 || element.MaxOccursString.ToLower() == "unbounded")
              {
                string propName = ClassGenerator.MakePluralPropertyName(ComplexType.Name, element.Name);

                cx.WL("");
                cx.WL("            {1} elem{0} = new {1}(node);", element.Name, element.ElementSchemaType.Name);
                cx.WL("            if (elem{0} != null)", element.Name);
                cx.WL("              {0}.Add(elem{1});", propName, element.Name);
              }
              else
              {
                string propName = ClassGenerator.MakePropertyName(ComplexType.Name, element.Name);

                cx.WL("            {0} = new {1}(node);", propName, element.ElementSchemaType.Name);
              }

              cx.WL("            break;");
            }
          }
          else
          {
            foreach (string name in subSection.ElementNames)
              cx.WL("          case \"{0}\":", name.ToLower());

            cx.WL("            ParseSection{0}Nodes(parent, ref enumerator, ref more);", subSection.Id);
            cx.WL("            break;");
            cx.WL("");
          }
        }

        cx.WL("");
        cx.WL("          default:");
        cx.WL("            done = true;");
        cx.WL("            break;");

        cx.WL("        }");
        cx.WL("");
        cx.WL("        if (!done)");
        cx.WL("          more = enumerator.MoveNext();");
        cx.WL("      }");
        cx.WL("    }");
        cx.WL("    #endregion");

        foreach (Section subSection in SubSections)
          subSection.GenerateParseNodesMethod(cx);
      }
      #endregion
    }
    #endregion

    #region class ChoiceSection
    private class ChoiceSection : Section
    {
      // Public Constructors

      #region ChoiceSection(ClassGenerator classGenerator)
      public ChoiceSection(ClassGenerator classGenerator) : base(classGenerator)
      {
      }

      #endregion

      // Public Methods

      #region GenerateParseNodesMethod(IGeneratorContext cx)
      public override void GenerateParseNodesMethod(IGeneratorContext cx)
      {
        cx.WL("");
        cx.WL("    #region ParseSection{0}Nodes(XmlNode parent, ref IEnumerator enumerator, ref bool more)", Id);
        cx.WL("    private void ParseSection{0}Nodes(XmlNode parent, ref IEnumerator enumerator, ref bool more)", Id);
        cx.WL("    {");

        int choice = 0;

        cx.WL("      int choice = -1;");
        cx.WL("");
        cx.WL("      for (bool done=false; more && !done;)");
        cx.WL("      {");
        cx.WL("        XmlNode node = enumerator.Current as XmlNode;");
        cx.WL("        if (node.NodeType == XmlNodeType.Comment)");
        cx.WL("        {");
        cx.WL("          more = enumerator.MoveNext();");
        cx.WL("          continue;");
        cx.WL("        }");
        cx.WL("");
        cx.WL("        switch (node.Name.ToLower())");
        cx.W("        {");

        foreach (Section subSection in SubSections)
        {
          ElementsSection es = subSection as ElementsSection;
          if (es != null)
          {
            foreach (XmlSchemaElement element in es.Elements)
            {
              cx.WL("");
              cx.WL("          case \"{0}\":", element.Name.ToLower());
              cx.WL("            if (choice >= 0 && choice != {0})", choice);
              cx.WL("              throw new Exception(\"Multiple elements matching a schema choice\");");
              cx.WL("");
              cx.WL("            choice = {0};", choice);


              if (element.MaxOccurs > 1 || element.MaxOccursString.ToLower() == "unbounded")
              {
                string propName = ClassGenerator.MakePluralPropertyName(ComplexType.Name, element.Name);

                cx.WL("");
                cx.WL("            {1} elem{0} = new {1}(node);", element.Name, element.ElementSchemaType.Name);
                cx.WL("            if (elem{0} != null)", element.Name);
                cx.WL("              {0}.Add(elem{1});", propName, element.Name);
              }
              else
              {
                string propName = ClassGenerator.MakePropertyName(ComplexType.Name, element.Name);

                cx.WL("            {0} = new {1}(node);", propName, element.ElementSchemaType.Name);
              }

              cx.WL("            break;");

              choice++;
            }
          }
          else
          {
            cx.WL("");

            foreach (string name in subSection.ElementNames)
              cx.WL("          case \"{0}\":", name.ToLower());

            cx.WL("            if (choice >= 0 && choice != {0})", choice);
            cx.WL("              throw new Exception(\"Multiple elements matching a schema choice\");");
            cx.WL("");
            cx.WL("            choice = {0};", choice);
            cx.WL("            ParseSection{0}Nodes(parent, ref enumerator, ref more);", subSection.Id);
            cx.WL("            break;");
          }

          choice++;
        }

        cx.WL("");
        cx.WL("          default:");
        cx.WL("            done = true;");
        cx.WL("            break;");

        cx.WL("        }");
        cx.WL("");
        cx.WL("        if (!done)");
        cx.WL("          more = enumerator.MoveNext();");
        cx.WL("      }");
        cx.WL("    }");
        cx.WL("    #endregion");

        foreach (Section subSection in SubSections)
          subSection.GenerateParseNodesMethod(cx);
      }
      #endregion
    }
    #endregion

    #region class ElementsSection
    private class ElementsSection : Section
    {
      public List<XmlSchemaElement> Elements = new List<XmlSchemaElement>();

      // Public Constructors

      #region ElementsSection(ClassGenerator classGenerator)
      public ElementsSection(ClassGenerator classGenerator) : base(classGenerator)
      {
      }
      #endregion

      // Public Methods

      #region GenerateParseNodesMethod(IGeneratorContext cx)
      public override void GenerateParseNodesMethod(IGeneratorContext cx)
      {
      }
      #endregion

      #region AddElementNames(List<string> names)
      public override void AddElementNames(List<string> names)
      {
        foreach (XmlSchemaElement element in Elements)
          names.Add(element.Name);

        base.AddElementNames(names);
      }
      #endregion
    }
    #endregion
  }
}
